package com.zkh.myutils.bean;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;

import com.zkh.myutils.annotation.Format;
import com.zkh.myutils.utils.ArrayUtils;
import com.zkh.myutils.utils.StringUtils;

/**
 * 数组/集合转换器
 * @author zkh
 */
public class BeanArrayConverter {

	/**
	 * 集合类型
	 */
	public static List<String> collectionType = new ArrayList<String>(Arrays.asList(
		"java.util.Collection", "java.util.List", "java.util.ArrayList", "java.util.LinkedList", "java.util.Vector", 
		"java.util.Set", "java.util.HashSet", "java.util.TreeSet"
	));
	
	/**
	 * Map值转bean属性值
	 * @param value Map值
	 * @param mapValueClass value.class值
	 * @param beanAttrClass bean属性类型
	 * @param gtype 泛型类型。若没有使用泛型，则统一视为泛型Object。
	 * @return
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	public static Object toList(Object value, Class<?> mapValueClass, Field field, Class<?> beanAttrClass, Type gtype) {
		//值为空
		if(value==null) {
			return null;
		}
		//值类型
		String typename = mapValueClass.getName();
		boolean isarr = mapValueClass.isArray(), iscoll = collectionType.contains(typename);
		//目标类型
		String tgt = beanAttrClass.getName();
		//不是集合或数组
		if(!isarr && !iscoll) {
			throw new BeanTransException(typename+"类型无法转换为" + tgt + "类型");
		}
		//类型转换
		Collection coll = iscoll ? (Collection) value : ArrayUtils.asList(ArrayUtils.getArray(value));
		//创建集合容器，用于返回
		Collection listctn = coll;
		try {
			//使用了泛型（未使用泛型，直接返回原集合）
			if(gtype instanceof ParameterizedType) {
				//创建集合容器，用于返回
				listctn = (Collection) (("java.util.Set".equals(beanAttrClass.getName()) ? new HashSet() : (beanAttrClass.isInterface() ? new ArrayList() : beanAttrClass.newInstance())));
				//泛型类型对象
				Type type = ((ParameterizedType) gtype).getActualTypeArguments()[0];
				//获取泛型类型的Class
				try {
					//泛型类型Class
					Class<?> gtypeClass = Class.forName(type.toString().substring(6));
					//泛型类型
					String simpleName = gtypeClass.getSimpleName(), typeName = gtypeClass.getName();
					//遍历器
					Iterator<?> iter = coll.iterator();
					//自定义解析器
					TypeConverterItem<?> coverter = BeanUtils.converter.getConverter(gtypeClass);
					//有解析器
					if(coverter!=null) {
						while(iter.hasNext()) {
							listctn.add(coverter.convertToProperty(iter.next()));
						}
					}
					//字符串
					else if("java.lang.String".equals(typeName)) {
						//遍历值
						while(iter.hasNext()) {
							Object val = iter.next();
							listctn.add(val==null ? null : val.toString());
						}
					}
					//泛型为不继续解析的值
					else if(BeanBaseTypeConverter.notParseType.contains(simpleName)) {
						//遍历值
						while(iter.hasNext()) {
							listctn.add(BeanBaseTypeConverter.toBaseType(simpleName, iter.next()));
						}
					}
					//日期
					else if(StringUtils.isExist(typeName, "java.util.Date", "java.sql.Timestamp")) {
						//默认解析格式
						String pattern = "yyyy-MM-dd HH:mm:ss";
						//获取是否有注解
						Format fmt = field!=null ? field.getAnnotation(Format.class) : null;
						//有注解
						if(fmt!=null) {
							pattern = fmt.pattern();
						}
						//类型
						boolean isDate = "java.util.Date".equals(typeName);
						//日期转换对象
						SimpleDateFormat sdf = new SimpleDateFormat(pattern);
						//遍历
						while(iter.hasNext()) {
							//值
							Object obj = iter.next();
							try {
								//获取值并解析为对应的日期类型
								Date date = obj==null ? null : 
									(isDate ? sdf.parse(obj.toString()) : new Timestamp(sdf.parse(obj.toString()).getTime()));
								//保存
								listctn.add(date);
							}catch(ParseException e) {
								throw new BeanTransException("日期类型解析失败", e);
							}
						}
					}else {
						//其它类型
						//遍历
						while(iter.hasNext()) {
							Object val = iter.next();
							if(val instanceof Map) {
								listctn.add(BeanUtils.mapToBean((Map<String, ?>) val, gtypeClass));
							}else {
								throw new BeanTransException("类型"+val.getClass().getName()+"无法转换为类型"+typeName);
							}
						}
					}
				} catch (ClassNotFoundException e) {
					//泛型类型不存在
					throw new BeanTransException("泛型类型不存在", e);
				}
			}
		} catch (InstantiationException | IllegalAccessException e) {
			throw new BeanTransException(e);//该异常不会执行到
		}
		//返回集合
		return listctn;
	}

	/**
	 * Map值转bean属性值
	 * @param value Map值
	 * @param mapValueClass value.class值
	 * @param beanAttrClass bean属性类型，此处beanAttrClass.isArray()为true
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Object toArray(Object value, Class<?> mapValueClass, Field field, Class<?> beanAttrClass) {
		//bean属性类型
		String attrName = beanAttrClass.getName();
		//类型相同，直接返回值
		if(value==null || mapValueClass.getName().equals(attrName)) {
			return value;
		}
		//值是否为集合/数组
		boolean isArr = mapValueClass.isArray();
		boolean isColl = collectionType.contains(mapValueClass.getName());
		//强转集合
		Collection<?> coll = isColl ? (Collection<?>) value : null;
		//值不是数组也不是集合
		if(!isArr && !isColl) {
			throw new BeanTransException(mapValueClass.getName()+"不能转换为" + attrName + "类型");
		}
		//实体属性的数组类型
		boolean baseArr = ArrayClass.inArrayClass(attrName);	//基础数组
		boolean dateArr = baseArr ? false : StringUtils.isExist(attrName, "[Ljava.util.Date;", "[Ljava.sql.Timestamp;");//日期数组
		//临时值
		Object[] vals = baseArr || dateArr || isColl ? (isArr ? ArrayUtils.getArray(value) : new Object[coll.size()]) : null;
		//如果是集合，将集合转为数组
		if(isColl) {
			//计数
			int count = 0;
			//遍历
			Iterator<?> ite = coll.iterator();
			//遍历
			while(ite.hasNext()) {
				//值
				Object obj = ite.next();
				//保存
				vals[count++] = baseArr || dateArr ? (obj==null ? null : obj.toString()) : obj;
			}
		}
		//长度
		int len = isArr ? Array.getLength(value) : coll.size();
		//自定义解析器
		TypeConverterItem<?> coverter = BeanUtils.converter.getConverter(value.getClass().getComponentType());
		//有解析器
		if(isArr && coverter!=null) {
			//获取当前类型
			ArrayClass arrayClass = ArrayClass.getArrayClassByclass(attrName);
			//创建数组
			Object arr = Array.newInstance(arrayClass.cls, len);
			//保存值
			for(int i=0; i<len; i++) {
				Array.set(arr, i, coverter.convertToProperty(vals[i]));
			}
			//返回
			return arr;
		}
		//基础数组
		else if(baseArr) {
			//获取当前类型
			ArrayClass arrayClass = ArrayClass.getArrayClassByclass(attrName);
			//创建数组
			Object arr = Array.newInstance(arrayClass.cls, len);
			//保存值
			for(int i=0; i<len; i++) {
				Array.set(arr, i, BeanBaseTypeConverter.toBaseType(arrayClass.cls.getSimpleName(), vals[i]));
			}
			//返回
			return arr;
		}
		//日期
		else if(dateArr) {
			//具体类型
			boolean isDate = "[Ljava.util.Date;".equals(attrName);
			//创建数组
			Date[] arr = isDate ? new Date[len] : new Timestamp[len];
			//默认解析格式
			String pattern = "yyyy-MM-dd HH:mm:ss";
			//获取是否有注解
			Format fmt = field!=null ? field.getAnnotation(Format.class) : null;
			//有注解
			if(fmt!=null) {
				pattern = fmt.pattern();
			}
			//日期转换对象
			SimpleDateFormat sdf = new SimpleDateFormat(pattern);
			try {
				//保存值
				for(int i=0; i<len; i++) {
					//值
					String tempval = (String) vals[i];
					//保存值
					arr[i] = tempval==null ? null : (isDate ? sdf.parse(tempval) : new Timestamp(sdf.parse(tempval).getTime()));
				}
				//返回
				return arr;
			}catch(ParseException e) {
				throw new BeanTransException("日期类型解析失败", e);
			}
		}
		//其它类型处理
		else {
			//数组类型
			Class<?> cls = beanAttrClass.getComponentType();
			//创建数组
			Object arr = Array.newInstance(cls, len);
			//保存值
			for(int i=0; i<len; i++) {
				Object val = vals[i];
				//为空
				if(val==null) {
					Array.set(arr, i, null);
				}
				//字符串
				else if("java.lang.String".equals(cls.getName())) {
					Array.set(arr, i, val.toString());
				}
				//Map对象
				else if(val instanceof Map) {
					Array.set(arr, i, BeanUtils.mapToBean((Map<String, ?>) val, beanAttrClass));
				}else {
					throw new BeanTransException("类型"+mapValueClass.getName()+"无法转换为类型"+attrName);
				}
			}
			//返回
			return arr;
		}
	}
}
